<?php
namespace gnomephp\crud;

class DoctrineEntity{
	const IDENT_SEPARATOR = '-_-_-';


	/**
	 * @var Doctrine\ORM\EntityManager
	 */
	protected $em;
	
	/**
	 * Holds the namespaced class name. 
	 * This is including the application namespace.
	 * @var string
	 */
	protected $ns;

	/**
	 * Holds the object of the entity.
	 * @var \Object
	 */
	protected $obj;
	
	
	/**
	 * Holds the metadata of the entity.
	 * @var Doctrine\ORM\Mapping\ClassMetadata
	 */
	public $meta;

	public function __construct($em, $ns){
		$this->em = $em;
		$this->ns = $ns;


		$this->meta = $this->em->getMetadataFactory()->getMetadataFor($ns);

		$this->obj = $this->createObject();
	}
	
	public function createObject(){
		
		$obj = $this->ns;
		$refClass   = new \ReflectionClass( $obj );
		
		
		$constructor = $refClass->getConstructor();
		
		$params = $constructor->getParameters();
		
		// Build parameters.
		$paramArray = array();
		for($i = 0; $i <= count($params); $i++){
			$paramArray[] = null;
		}
		

		return $refClass->newInstanceArgs($paramArray);
	}
	
	public function getFields(){
		$refObject   = new \ReflectionObject( $this->obj );
		
		
		if ($refObject->implementsInterface('gnomephp\crud\FieldAccess')){
			$ar = $this->meta->fieldMappings;
			
			foreach($ar as $k => $v){
				if (!in_array($k, $this->obj->crudFields()))unset($ar[$k]);
			}
			
			return $ar;
		}
		
		return $this->meta->fieldMappings;
	}

	public function translateField($field){
		$obj = $this->createObject();
		$ar = $obj->getFieldNames();
		return  isset($ar[$field]) ? $ar[$field] : $field;
	}
	
	public function getEntries(){
		$query = $this->em->createQuery("SELECT fieldGnomePHP FROM {$this->ns} fieldGnomePHP");
		$entries = $query->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);

		return $entries;
	}

	public function getEntryWhereId($id, $hydrate = true){
		// Create query.
		$query = $this->em->createQuery("SELECT fieldGnomePHP FROM {$this->ns} fieldGnomePHP {$this->generateWhere($id)}");

		// Fetch result.
		$entry = $query->getResult($hydrate ? \Doctrine\ORM\Query::HYDRATE_ARRAY : null);
		return $entry[0];
	}

	public function updateEntry($id, $vars){
		
		$query = $this->em->createQuery("UPDATE {$this->ns} fieldGnomePHP ".$this->varsToDQL($vars)." {$this->generateWhere($id)}");	
		$query->execute();
		
		// Create query.
		$query = $this->em->createQuery("SELECT fieldGnomePHP FROM {$this->ns} fieldGnomePHP {$this->generateWhere($id)}");

		// Fetch result.
		$entry = $query->getOneOrNullResult();
		if ($entry !== null){
			$refObject   = new \ReflectionObject( $entry );
			if ($refObject->implementsInterface('gnomephp\crud\OnUpdateListener')){
				$entry->onUpdate();
			}
			
			$this->em->persist($entry);
			
			$this->em->flush();
		}
		return false;
	}

	public function deleteEntry($id){
		// Create query.
		$query = $this->em->createQuery("SELECT fieldGnomePHP FROM {$this->ns} fieldGnomePHP {$this->generateWhere($id)}");

		// Fetch result.
		$entry = $query->getOneOrNullResult();
		if ($entry !== null){
			$refObject   = new \ReflectionObject( $entry );
			if ($refObject->implementsInterface('gnomephp\crud\OnDeleteListener')){
				$entry->onDelete();
			}
			
			$this->em->persist($entry);
			
			$this->em->flush();
		}
		// Create query.
		$query = $this->em->createQuery("DELETE FROM {$this->ns} fieldGnomePHP {$this->generateWhere($id)}");
		return $query->execute();

	}

	public function createEntry($data){
		$this->validateFields($this->meta->fieldMappings, $data);
		
		$entity = $this->ns;
		$obj = new $entity;
		$refObject   = new \ReflectionObject( $obj );
		foreach($data as $key => $val){
			if (!$val)$val = null;
			$refProperty = $refObject->getProperty( $key );
			$refProperty->setAccessible( true );
			$refProperty->setValue($obj, $val);
		}
		
		if ($refObject->implementsInterface('gnomephp\crud\OnCreateListener')){
			$obj->onCreate();
		}
		
			
		$this->em->persist($obj);
			
		$this->em->flush();

	}

	public function getIdentifier($entry){
		$uid = '';
		foreach($this->meta->identifier as $ident){
			$uid .= $entry[$ident] . DoctrineEntity::IDENT_SEPARATOR;
		}
			
		return $uid;
	}

	public function generateWhere($id){
		$idents = $this->meta->identifier;
		$bits = explode(DoctrineEntity::IDENT_SEPARATOR, $id);
		$where = 'WHERE ';
		foreach($idents as $k => $ident){
			$where .= ($k != 0 ? ' AND ' : '').'fieldGnomePHP.'.$ident . ' = ' . "'".$bits[$k]."'";
		}
		return $where;
	}
	
	
	public function getRelations($id=false){

		$ret = array();
		
		$rels = $this->meta->associationMappings;
		
		if ($id){
			$entry = $this->getEntryWhereId($id, false);
			$refObject   = new \ReflectionObject( $entry );
		
			
		}else{
			$entry = null;
		}
		
		
		foreach($rels as $key => $info){
			
			$rel = new DoctrineEntity($this->em, $info['targetEntity']);
			
			if ($entry !== null){
				$value = null;				
			}else{
				$value = null;
			}
		
			
			
			$ret[$key] = array(
				'value' => $value,
				'key' => $key,
				'list' => $rel->getEntries(),
				'idkey' => $rel->meta->identifier[0],
				'fields' => $rel->getFields(),
				'worker' => $rel
			);
		}
		
		
		
		return $ret;
	}

	protected function varsToDQL($array){
		$dql = 'SET ';
		foreach($array as $k => $v){
			$dql .= 'fieldGnomePHP.' . $k . '=' . ($v != '' ? "'".$v."'" : 'NULL'). ",";
		}
		$dql = substr($dql, 0, -1).' ';

		return $dql;
	}


	public function validateFields($fields, $posts){
		foreach($posts as $name => $val){
			$info = $fields[$name];
			$f = $val;
			if ($info['length'])$f->maxLength($info['length']);
			if ($info['type']){
				switch($info['type']){
					case 'integer':
						$f->numeric();
						break;
					case 'date':
					case 'datetime':
					case 'time':
						if (!strtotime($val)){
							throw new \gnomephp\input\DataFieldValidException(array("$name is not a valid date. Please define a valid date."));
						}
						break;
					case 'float':
						$f->float();
						break;
					case 'boolean':
						if ($val != 0 && $val != 1){
							throw new \gnomephp\input\DataFieldValidException(array("$name is not a valid boolean type."));
						}
						break;
				}
			}
			$f->validate();
		}
	}

}